package org.example.springoauth2.securitycommon.config.authenticate;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.springoauth2.securitycommon.config.authenticate.handler.CustomizeAuthenticationFailureHandler;
import org.example.springoauth2.securitycommon.config.authenticate.handler.CustomizeAuthenticationSuccessHandler;
import org.example.springoauth2.securitycommon.config.authenticate.handler.FormAuthenticationFailureHandler;
import org.example.springoauth2.securitycommon.config.authenticate.handler.SsoLogoutSuccessHandler;
import org.example.springoauth2.securitycommon.factory.SysUserFactory;
import org.example.springoauth2.securitycommon.service.SysUserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;

/**
 * 由于有可能引起循环依赖 所以单独提出来配置
 *
 * @author hzq
 * @since 2021-09-13
 */
@Configuration
public class OtherAuthorizationSecurityConfiguration {

    /**
     * token 存储
     */
    @Bean
    public TokenStore tokenStore() {
        // 这里可以基于RedisTokenStore配置 可以实现租户隔离的key
        InMemoryTokenStore tokenStore = new InMemoryTokenStore();
        // tokenStore.setAuthenticationKeyGenerator(new DefaultAuthenticationKeyGenerator() {
        //     @Override
        //     public String extractKey(OAuth2Authentication authentication) {
        //         return super.extractKey(authentication) + StrUtil.COLON + TenantHolder.get();
        //     }
        // });
        return tokenStore;
    }

    /**
     * token 增强
     */
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new EnhanceTokenEnhancer();
    }

    /**
     * 授权服务器令牌服务--默认实现
     */
    @Bean
    public DefaultTokenServices defaultTokenServices(TokenStore tokenStore, TokenEnhancer tokenEnhancer, SysUserService sysUserService) {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        // 必须设置token存储 不然无法启动
        defaultTokenServices.setTokenStore(tokenStore);
        // 这里需要 设置增强器 不然自定义登录无法使用增强token CustomizeAuthenticationSuccessHandler 这里使用
        defaultTokenServices.setTokenEnhancer(tokenEnhancer);
        defaultTokenServices.setSupportRefreshToken(true);
        defaultTokenServices.setRefreshTokenValiditySeconds(300);
        defaultTokenServices.setAccessTokenValiditySeconds(30);
//        addUserDetailsService(defaultTokenServices, sysUserService);
        return defaultTokenServices;
    }

    /**
     * 由于刷新token 只用username及unionId 去loadUserByUsername(unionId)
     * 此时拿不到 authType
     * 所以不配置 及不设置AuthenticationManager
     * <p>
     * 刷新token使用
     *
     * @see org.springframework.security.oauth2.provider.token.DefaultTokenServices#refreshAccessToken(String, TokenRequest)
     * @see org.springframework.security.authentication.ProviderManager#authenticate(Authentication)
     * @see org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider#authenticate(Authentication)
     * @see org.example.springoauth2.securitycommon.service.SysUserService#loadUserByUsername(String)
     */
    private void addUserDetailsService(DefaultTokenServices tokenServices, UserDetailsService userDetailsService) {
        if (userDetailsService != null) {
            PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
            provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService));
            tokenServices.setAuthenticationManager(new ProviderManager(Collections.singletonList(provider)));
        }
    }

    /**
     * 表单认证失败处理器
     */
    @Bean
    public FormAuthenticationFailureHandler formAuthenticationFailureHandler() {
        return new FormAuthenticationFailureHandler();
    }

    /**
     * sso 退出成功处理器
     */
    @Bean
    public SsoLogoutSuccessHandler ssoLogoutSuccessHandler() {
        return new SsoLogoutSuccessHandler();
    }

    /**
     * 自定义认证失败处理器
     */
    @Bean
    public CustomizeAuthenticationFailureHandler customizeAuthenticationFailureHandler(ObjectMapper objectMapper) {
        return new CustomizeAuthenticationFailureHandler(objectMapper);
    }

    /**
     * 自定义认证成功处理器
     *
     * @param passwordEncoder      密码处理
     * @param clientDetailsService 客户端服务
     * @param defaultTokenServices 授权服务
     */
    @Bean
    public CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler(PasswordEncoder passwordEncoder,
                                                                                       ObjectMapper objectMapper,
                                                                                       ClientDetailsService clientDetailsService,
                                                                                       DefaultTokenServices defaultTokenServices) {
        return new CustomizeAuthenticationSuccessHandler(passwordEncoder, objectMapper, clientDetailsService, defaultTokenServices);
    }

    /**
     * 密码处理
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    /**
     * rest 模板
     */
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    /**
     * 用户详情服务
     */
    @Bean
    public SysUserService sysUserService() {
        return new SysUserService();
    }

    /**
     * 模拟数据
     */
    @Bean
    public SysUserFactory sysUserFactory() {
        return new SysUserFactory();
    }
}
